/*
 * @(#)ArchitectureComponent.java	Robert Denda&Didier Gutacker
 *
 */

import java.lang.*;
import java.awt.*;
import java.awt.image.*;


/** 
 * Die abstrakte Klasse ArchitectureComponent stellt die Basisfunktionalität 
 * für die benötigten Netzwerkkomponenten zur Verfügung.
 *
 * @author 	Robert Denda
 * @version     1.1, 05.09.1997
 * @see Verbindung
 * @see Knoten
 * @see SimulationObject
 * @see GraphicalElement
 */

public abstract class ArchitectureComponent implements Runnable, GraphicalElement, ImageObserver {
  protected int xpos;		// current x position
  protected int ypos;		// current y position
  protected int height;		// component's height
  protected int width;		// component's width
  protected int absXPos, absYPos;

  protected Graphics gc;	// graphics context

  protected Thread t;		// object's thread

  protected SimulationObject net;	// network

  protected int id;		// id of the architecture component
  protected boolean marked;	// marked flag
  protected boolean wakeUpable;
  protected boolean paintRequested; // flag indicating paintRequested
  protected boolean finished = false;

  protected boolean initialized = false;
  protected TextMethods text;

  protected Lock sizeChangeLock;

  debug DEBUG = new debug();
  
  

  public ArchitectureComponent( int id, SimulationObject net, Graphics gc ) {
    this( id, net, gc, -1, -1, 1, 1 );
  } // ArchitectureComponent.ArchitectureComponent

  public ArchitectureComponent( int id, SimulationObject net, Graphics gc, int xpos, int ypos, int width, int height ) {
    this.xpos = xpos - net.getXOffset();
    this.ypos = ypos - net.getYOffset();
    this.width = width;
    this.height = height;
    this.net = net;
    this.gc = gc;
    this.id = id;
    wakeUpable = true;
    marked = false;
    paintRequested = false;
    sizeChangeLock = new Lock();
    text = this.net.getSimulation().getText();
    absXPos = xpos;
    absYPos = ypos;
    t = new Thread( net.getThreadGroup(), this );
    t.setPriority( Thread.MIN_PRIORITY );
    t.start();
    initialized = true;
  } // ArchitectureComponent.ArchitectureComponent

  /** 
   * Beendet den eigenen Thread und gibt Resourcen frei
   */

  synchronized public void finishUp() {
    gc.clearRect( xpos, ypos, width, height);
    finished = true;
    text = null;
    sizeChangeLock = null;
    gc = null;
    net = null;
    t.stop();
    t = null;
  } // ArchitectureCompoenent.finishUp

  /**
   * Liefert wahr false das Objekt offiziell beendet wurde
   */

  public final boolean finished() {
    return finished;
  } // ArchitectureComponent.finished

  // methods from Runnable
  synchronized public void run() {
    for ( ;; ) {
      wakeUpable = false;	// already awake
      paint();
      wakeUpable = true;
      try {
	wait();
      } catch ( InterruptedException e ) {
      } // try
    } // for   
  } // ArchitectureComponent.run

  /**
   * Akquiriert alle für das Objekt zur Verfügung stehenden Locks
   *
   * @see ArchitectureComponent#releaseLocks
   */

  public void acquireLocks() {
    sizeChangeLock.acquire();
  } // ArchitectureComponent.acquireLocks

  /**
   * Gibt zuvor mit acquireLocks() akquirierte Locks wieder frei
   *
   * @see ArchitectureComponent#acquireLocks
   */

  public void releaseLocks() {
    sizeChangeLock.release();
  } // ArchitectureComponent.releaseLocks

  /**
   * Weckt den zuvor mit suspend() schlafengelegten Thread wieder auf
   *
   * @see ArchitectureComponent#suspend
   */
  
  public void resume() {
    t.resume();
  } // ArchitectureComponent.resume

  /**
   * Legt den objekteigenen Thread schlafen
   *
   * @see ArchitectureComponent#resume
   */

  public void suspend() {
    t.suspend();
  } // ArchitectureComponent.suspend

  // methods from GraphicalElement

  abstract void paint();	// implemented in subclass

  /**
   * Liefert wahr falls die übergebenen Koordinaten sich im Bereich des Objektes
   * befinden
   */

  public boolean atLocation( int x, int y ) {
    if ( absXPos <= x && x <= ( absXPos + width ) && absYPos <= y && y <= ( absYPos + height ) ) {
      return true;
    } else {
      return false;
    } // if
  } // ArchitectureComponent.atLocation

  /**
   * Signalisiert dem objekteigenen Thread, daß er das Objekt neu zeichnen soll
   */

  public final void paintRequest() {
    while ( !initialized ) {
      Thread.yield();
    } // while
    text = this.net.getSimulation().getText(); // possibly change language
    paintRequested = true;
    if ( wakeUpable ) {
      wakeUp();
    } // if
  } // ArchitectureComponent.paintRequest
    
  /**
   * Wecke den objekteigenen Thread auf
   */

  synchronized final public void wakeUp() {
    notifyAll();
  } // ArchitectureComponent.wakeUp

  /**
   * Setze die aktuelle Position auf die übergebenen Koordinaten
   *
   * THREADSAFE
   */

  public final  void setPosition( int x, int y ) {
    sizeChangeLock.acquire();
    xpos = x - net.getXOffset();		
    ypos = y - net.getYOffset();		
    absXPos = x;
    absYPos = y;
    sizeChangeLock.release();
  } // ArchitectureComponent.setPosition

  /**
   * Liefert die aktuelle X-Koordinate
   *
   * THREADSAFE
   */

  public final  int getPositionX() {
    sizeChangeLock.acquire();
    int x = absXPos;
    sizeChangeLock.release();
    return x;
  } // ArchitectureComponent.getPositionX

  
  /**
   * Liefert die aktuelle Y-Koordinate
   *
   * THREADSAFE
   */

  public final  int getPositionY() {
    sizeChangeLock.acquire();
    int y = absYPos;
    sizeChangeLock.release();
    return y;
  } // ArchitectureComponent.getPositionY

  /** 
   * Setzt die aktuelle Breite 
   *
   * THREADSAFE
   */

  public final  void setWidth( int w ) {
    sizeChangeLock.acquire();
    width = w;
    sizeChangeLock.release();
  } // ArchitectureComponent.setWidth

  /** 
   * Liefert die aktuelle Breite
   *
   * THREADSAFE
   */

  public final  int getWidth() {
    sizeChangeLock.acquire();
    int w = width;
    sizeChangeLock.release();
    return w;
  } // ArchitectureComponent.getWidth

  /** 
   * Liefert die aktuelle Höhe
   *
   * THREADSAFE
   */

  public final  int getHeight() {
    sizeChangeLock.acquire();
    int h = height;
    sizeChangeLock.release();
    return h;
  } // ArchitectureComponent.getHeight

  /**
   * Setzt die aktuelle Höhe
   *
   * THREADSAFE
   */

  public final  void setHeight( int h ) {
    sizeChangeLock.acquire();
    height = h;
    sizeChangeLock.release();
  } // ArchitectureComponent.setHeight

  /**
   * Signalisiert, daß das Objekt markiert ist
   */

  synchronized public void mark() {
    marked = true;
  } // ArchitectureComponent.mark

  /** 
   * Setzt Markierung zurück
   */

  synchronized public void unmark() {
    marked = false;
  } // ArchitectureComponent.unmark

  /**
   * Liefert die Identifikationsnummer der Komponente
   */

  public final  int getID() {
    return id;
  } // ArchitectureComponent.getID

  /** 
   * Liefert das Netzwerk, zu dem die Komponente gehört
   */

  public final SimulationObject getNet() {
    return net;
  } // ArchitectureComponent.getNet

  public boolean imageUpdate( Image image, int flags, int x, int y, int width, int height ) {
    if ( ( flags & ABORT ) != 0 ) {
      System.err.println( "imageUpdate: image loading aborted" );
      return false;
    } // if
    if ( ( flags & ALLBITS ) != 0 ) { // image downloaded ?
      return false;
    } // if
    paintRequest();
    return true;
  } // ArchitectureComponent.updateImage

} // ArchitectureComponent














